package top.tangyh.lamp.common.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;

import java.io.Serializable;
import java.util.*;

/**
 * <p>
 * List 工具，重点是提供行转列
 * </p>
 *
 * @author:
 * @since:
 */
public class ConfDataModelUtil {
	/**
	 * 配置式业务数据模析时，行转列.
	 *
	 * @param srcList       数据源
	 * @param busiKey       业务数据主键
	 * @param confCodeField 配置的编码字段名
	 * @param confValField  配置值字段名
	 * @param <T>
	 * @return
	 */
	public static <T> List<Map> pivotByConf(List<T> srcList, String busiKey, String confCodeField, String confValField) {
		if (srcList == null || srcList.isEmpty()
				|| StrUtil.isBlank(busiKey)
				|| StrUtil.isBlank(confCodeField)
				|| StrUtil.isBlank(confValField)
		) {
			return ConvertUtil.convertList(srcList, Map.class);
		}

		List<Map> rst = new ArrayList<>();
		Map<String, Map> dstMap = new HashMap<>();
		for (T item : srcList) {
			Map itemMap = BeanUtil.copyProperties(item, LinkedHashMap.class);
			Map tempMap = null;
			String busiVal = Convert.toStr(itemMap.get(busiKey), "");
			String confCode = Convert.toStr(itemMap.get(confCodeField));
			Object confVal = itemMap.get(confValField);
			if (dstMap.containsKey(busiVal)) {
				tempMap = dstMap.get(busiVal);

			} else {
				tempMap = BeanUtil.copyProperties(itemMap, LinkedHashMap.class);
				tempMap.remove(confCodeField);
				tempMap.remove(confValField);
				dstMap.put(busiVal, tempMap);
				rst.add(tempMap);
			}
			if (StrUtil.isNotBlank(confCode)) {
				tempMap.put(confCode, confVal);
			}

		}

		return rst;
	}

	/**
	 * 把扁平LIST结构转成树
	 * 说明：  关皱点是 记  根节点由 业务主键确定，不是由parentKey为null确定. 另外节点的key在整根树不是唯 一的，由业务主键与节点key共同确定。
	 * 业务场景是： 一相表单是动态的，由多个表单项组成。所有记录由多个表单项构成。  实现时：表单项 由配配置表实现，具体记录由 记录表完成。
	 * 这时，记录表中的记录 就由 表单记录的识别号（上面说的业务主键） 与表单配置项（即上面说的节点key）才是唯 一的，这时 记发表中自身的id在树展示中无多大意义
	 *
	 * @param srcList             待转换的list
	 * @param srcBusiKeyName      源的业务关键字段. 同一条记录的 的有配置项的该字段值相同，但不同记发泊该值是不同的
	 * @param srcTreeKeyFieldName 源记录中的 树状结构的key值，与srcParentFieldName 一起描绘出树装结构
	 * @param srcParentFieldName  源记录的 父字段名，
	 * @param dstSonFieldName     目标记录即，返回结果的节点的 存放孩子的字段名
	 * @param dstClass            转换后的记录类型
	 * @param dstSonClass         转换后的子记录类型
	 * @param <S>                 源记录类型
	 * @param <D>                 目标记录类型
	 * @param <Son>               目标记录子类型
	 * @return
	 */
	public static <S, D, Son> List<D> toTree(List<S> srcList, String srcBusiKeyName, String srcTreeKeyFieldName, String srcParentFieldName, String dstSonFieldName, Class<D> dstClass, Class<Son> dstSonClass) {
		List<D> dst = new ArrayList<>();

		if (srcList == null || srcList.isEmpty()) return dst;
		Map<String, D> busiNodeCachMap = new HashMap<>();//业务节点缓存
		Map<String, Son> sonNodeCachMap = new HashMap<>();//子节点缓存

		Set<String> root = new HashSet<>();
		for (S s : srcList) {

			String busiKeyVal = Convert.toStr(BeanUtil.getProperty(s, srcBusiKeyName));
			if (StrUtil.isBlank(busiKeyVal)) continue;

			String pVal =Convert.toStr( BeanUtil.getProperty(s, srcParentFieldName));
			String treeKeyVal =Convert.toStr( BeanUtil.getProperty(s, srcTreeKeyFieldName));
			// 按业务key检查是否存在缓存
			D d = busiNodeCachMap.get(busiKeyVal);
			List<Son> sonList = null;
			if (d == null) {//业务节点缓存中不存在时，加入业务节点缓存中去。
				d = ReflectUtil.newInstance(dstClass.getName());//生成父点实例
				BeanUtil.copyProperties(s, d);
				busiNodeCachMap.put(busiKeyVal, d);
				dst.add(d);
				sonList = new ArrayList<>();
				BeanUtil.setProperty(d, dstSonFieldName, sonList);//给父节点 设置儿子集合实例,但是空的
			}

			sonList = BeanUtil.getProperty(d, dstSonFieldName);
			if (StrUtil.isBlank(pVal)) {//指向父节点的属性为空
				// 把源记录转成目标实例
				String sonCachKey = busiKeyVal + treeKeyVal;
				Son dSon = sonNodeCachMap.get(sonCachKey);
				if (dSon == null) {//子节点缓存中不存中，加入到子节点缓存中去
					dSon = ReflectUtil.newInstance(dstSonClass.getName());
					BeanUtil.copyProperties(s, dSon);
					sonNodeCachMap.put(sonCachKey, dSon);
					sonList.add(dSon);//把本节的 儿子集合增加值
				} else {
					//子节点缓存中存在，指向父节点的属生又为空，需要把本节点添加到业务节点中的孩子中去，
					// 原因是有可能时某个子孙节点先于父节点存在，提前生成了父节点缓存。提前生成的缓存，不知道是否需要加入到业务节点的孩子中，
					// 故这项工作留到这里完成
					List<Son> busiNodeSonList = BeanUtil.getProperty(d, dstSonFieldName);
					busiNodeSonList.add(dSon);
				}

			} else {//有指向父节点的属值
				String pValCachKey = busiKeyVal + pVal;
				Son tempParentNode = sonNodeCachMap.get(pValCachKey);
				if (tempParentNode == null) {
					//父节点 不在子节点缓存中， 说明节节点先于父节点存在，需要生成节点加入子节点缓存里
					//由于此时不知道是否 需要加入到业务节点孩子中，故这项工作 不做
					tempParentNode = ReflectUtil.newInstance(dstSonClass.getName());
					BeanUtil.copyProperties(s, tempParentNode);
//					BeanUtil.setProperty(tempParentNode, srcTreeKeyFieldName, pVal);
					sonNodeCachMap.put(pValCachKey, tempParentNode);
				}

				List<Son> list = BeanUtil.getProperty(tempParentNode, dstSonFieldName);
				if (list == null) {//父节点的孩子集合不存在，生成一个空的集合以保存孩子
					list = new ArrayList();
					BeanUtil.setProperty(tempParentNode, dstSonFieldName, list);
				}
				//把本节点加入到父节点的孩子中去
				Son dSon = ReflectUtil.newInstance(dstSonClass.getName());
				BeanUtil.copyProperties(s, dSon);
				list.add(dSon);
				//把本节点加入到孩子节点绥存中去
				sonNodeCachMap.put(busiKeyVal + treeKeyVal, dSon);
			}
		}
		return dst;
	}


	/**
	 *  对给定数组，按指定关键字进行分类。
	 * @param srcList 缩定数组
	 * @param keyFieldList  业务关关键值，按他们的值进行分类
	 * @param dstClass  目标数组,有孩子节点，注意仅仅是一层分类，孩子节点就是叶子节点
	 * @param sonClass  目标数组的孩子节点的类型
	 * @param <S>  源类型
	 * @param <D>  目的类型
	 * @param <Son> 目的类型中的孩子类型
	 * @return
	 */
	public static <S, D, Son> List<D> classify(List<S> srcList,List<String> keyFieldList,String dstSonFieldName,Class<D> dstClass,Class<Son> sonClass){
		if( srcList==null || srcList.isEmpty()) return new ArrayList<>();
		Map<String,List<Son>> cach=new LinkedHashMap<>();
		List<D> rstList=new ArrayList<>();
		for(S s:srcList){
			StringBuilder sb=new StringBuilder();
			for(String k:keyFieldList){
				sb.append(Convert.toStr(BeanUtil.getProperty(s,k),"")).append(";");
			}
			String sonListKey= sb.toString();
			List<Son>   sonList =cach.get(sonListKey);
			if(sonList==null){
				sonList=new ArrayList<>();
				cach.put(sonListKey,sonList);
				D d= ReflectUtil.newInstance(dstClass);

				BeanUtil.copyProperties(s,d);
				BeanUtil.setProperty(d,dstSonFieldName,sonList);
				rstList.add(d);
			}
			Son son =ReflectUtil.newInstance(sonClass);
			BeanUtil.copyProperties(s,son);
			sonList.add(son);
		}
		return rstList;
	}

	public static void main(String[] args){
		testtoTree();
	}


    public static void testtoTree() {

		List<Map> srcList = new ArrayList<>();
		Date date = new Date();
		Map<String, Object> map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", null);
		map.put("patient_ident", "lisi");
		map.put("order_ident", "配置1");
		map.put("item_code", "skin_sate");
		map.put("item_val", "皮肤完整");
		map.put("record_time", date);
		srcList.add(map);

		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "syringe_pump");
		map.put("patient_ident", "lisi");
		map.put("order_ident", "配置1");
		map.put("item_code", "drug_name");
		map.put("item_val", "青毒素");
		map.put("record_time", date);
		srcList.add(map);

		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", null);
		map.put("patient_ident", "lisi");
		map.put("order_ident", "配置1");
		map.put("item_code", "syringe_pump");
		map.put("item_val", "");
		map.put("record_time", date);
		srcList.add(map);


		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "syringe_pump");
		map.put("patient_ident", "lisi");
		map.put("order_ident", "配置1");
		map.put("item_code", "dose");
		map.put("item_val", "1ug");
		map.put("record_time", date);
		srcList.add(map);

		date = new Date();
		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", null);
		map.put("patient_ident", "zhangsan");
		map.put("order_ident", "配置2");
		map.put("item_code", "skin_sate");
		map.put("item_val", "皮肤偏红");
		map.put("record_time", date);
		srcList.add(map);

		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", null);
		map.put("patient_ident", "zhangsan");
		map.put("order_ident", "配置2");
		map.put("item_code", "syringe_pump");
		map.put("item_val", "");
		map.put("record_time", date);
		srcList.add(map);

		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "syringe_pump");
		map.put("patient_ident", "zhangsan");
		map.put("order_ident", "配置2");
		map.put("item_code", "drug_name");
		map.put("item_val", "庆大霉素");
		map.put("record_time", date);
		srcList.add(map);

		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "drug_name");
		map.put("patient_ident", "zhangsan");
		map.put("order_ident", "配置2");
		map.put("item_code", "drug_name1");
		map.put("item_val", "庆大霉素1");
		map.put("record_time", date);
		srcList.add(map);

		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "drug_name1");
		map.put("patient_ident", "zhangsan");
		map.put("order_ident", "配置2");
		map.put("item_code", "dose");
		map.put("item_val", "2ug");
		map.put("record_time", date);
		srcList.add(map);

		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "drug_name");
		map.put("patient_ident", "zhangsan");
		map.put("order_ident", "配置2");
		map.put("item_code", "drug_name2");
		map.put("item_val", "庆大霉素2");
		map.put("record_time", date);
		srcList.add(map);


		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "syringe_pump");
		map.put("patient_ident", "zhangsan");
		map.put("order_ident", "配置2");
		map.put("item_code", "dose");
		map.put("item_val", "2ug");
		map.put("record_time", date);
		srcList.add(map);

		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "syringe_pump");
		map.put("patient_ident", "wangwu");
		map.put("order_ident", "配置3");
		map.put("item_code", "dose");
		map.put("item_val", "3ug");
		map.put("record_time", date);
		srcList.add(map);


		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", null);
		map.put("patient_ident", "wangwu5");
		map.put("order_ident", "配置3");
		map.put("item_code", "syringe_pump");
		map.put("item_val", "");
		map.put("record_time", date);
		srcList.add(map);

		date = new Date();
		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", null);
		map.put("patient_ident", "wangwu");
		map.put("order_ident", "配置3");
		map.put("item_code", "skin_sate");
		map.put("item_val", "皮肤溃烂");
		map.put("record_time", date);
		srcList.add(map);


		map = new LinkedHashMap<>();
		map.put("id", IdUtil.createSnowflake(1l, 1l).nextIdStr());
		map.put("p_key", "syringe_pump");
		map.put("patient_ident", "wangwu");
		map.put("order_ident", "配置3");
		map.put("item_code", "drug_name");
		map.put("item_val", "头孢");
		map.put("record_time", date);
		srcList.add(map);

//		List<Map> rst=pivotByConf(srcList,"order_ident","item_code","item_val");

		List<Record> rst1 = toTree(srcList, "order_ident", "item_code", "p_key", "children", Record.class, Conf.class);

		System.out.printf("");
	}



	public static class Record implements Serializable {
		private String order_ident;
		private String patient_ident;
		private Date record_time;
		private List<Conf> children;

		public String getOrder_ident() {
			return order_ident;
		}

		public void setOrder_ident(String order_ident) {
			this.order_ident = order_ident;
		}

		public String getPatient_ident() {
			return patient_ident;
		}

		public void setPatient_ident(String patient_ident) {
			this.patient_ident = patient_ident;
		}

		public Date getRecord_time() {
			return record_time;
		}

		public void setRecord_time(Date record_time) {
			this.record_time = record_time;
		}

		public List<Conf> getChildren() {
			return children;
		}

		public void setChildren(List<Conf> children) {
			this.children = children;
		}
	}

	public static class Conf implements Serializable {
		private String item_code;
		private String item_val;
		private List<Conf> children;

		public String getItem_code() {
			return item_code;
		}

		public void setItem_code(String item_code) {
			this.item_code = item_code;
		}

		public String getItem_val() {
			return item_val;
		}

		public void setItem_val(String item_val) {
			this.item_val = item_val;
		}

		public List<Conf> getChildren() {
			return children;
		}

		public void setChildren(List<Conf> children) {
			this.children = children;
		}
	}

}
